package com.dmz.java.objectReference;

import java.lang.ref.PhantomReference;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;


/**
 *  java对象引象有四种类型,分别是：强引用、弱引用、软引用和虚引用
 * @author NUC
 *
 */
public class ObjectReference {

	public static void main(String[] args) {

		testSoftReference();

	}
	
	/**
	 * 强引用：一般new出来的对象都是强引用，强引用垃圾回收器不会收回它，如果想要被垃圾回收时，将引用设为null，当内存空间不足时会抛出OutOfMemoryError异常，使程序引常终止。在一个方法的内部有一个强引用，这个引用保存在Java栈中，而真正的引用内容(Object)保存在Java堆中。当这个方法运行完成后，就会退出方法栈，则引用对象的引用数为0，这个对象会被回收。但是如果这个strongReference是全局变量时，就需要在不用这个对象时赋值为null，因为强引用不会被垃圾回收。
	 */
	public static void testStrongReference() {
		//强引用
		Object strongReference = new Object();
		//弱化引用，使垃圾回收器能回收它
		strongReference = null;
	}
	
	/**
	 * 软引用：软引用的对象，在内存不足时会被垃圾回收器回收
	 * ReferenceQueue即这样的一个对象，当一个obj被gc掉之后，其相应的包装类，即ref对象会被放入queue中。我们可以从queue中获取到相应的对象信息，同时进行额外的处理。比如反向操作，数据清理等。
	 */
	public static void testSoftReference() {
		//虚拟机堆区大小被限制在10MB,然后分配了一个含有6MB的SoftReference类，然后当继续申请6MB堆区时，JVM就会回收掉softReference所指向的对象。
		ReferenceQueue<byte[]> referenceQueue = new ReferenceQueue<>();
		byte[] bytes = new byte[6*1024 *1024];
		SoftReference<byte[]> softReference = new SoftReference<>(bytes, referenceQueue);
	    // (先设置堆内存大小 -Xms10m -Xmx10m -XX:+PrintGCDetails)制造一个大对象，让堆内存溢出
	    try {
		    bytes = null;
		    Reference<? extends byte[]> reference = referenceQueue.poll();
		    System.out.println(softReference.get());//byte[]
		    System.out.println(reference); //null
            bytes = new byte[6*1024 *1024];
           
            reference = referenceQueue.poll();
     	    System.out.println(reference); //SoftReference
            System.out.println(softReference.get());//null
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
        	Reference<? extends byte[]> reference = referenceQueue.poll();
     	    System.out.println(reference); //null
            System.out.println(softReference.get()); //null
        }
	}
	/**
	 * 弱引用：只要垃圾回收器扫找描到就会被回收
	 */
	public static void testWeakReference() {
		Object o1 = new Object();
        WeakReference<Object> weakReference = new WeakReference<>(o1);
        o1 = null;

        System.out.println(o1);
        System.out.println(weakReference.get());
        System.gc();
        System.out.println("GC后===========");
        System.out.println(o1);
        System.out.println(weakReference.get());
	}
	
	public static void testWeakReference2() {
		Object o1=new Object();
		ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
        WeakReference<Object> weakReference = new WeakReference<>(o1, referenceQueue);

        System.out.println(o1);
        System.out.println(weakReference.get());
        System.out.println(referenceQueue.poll());

        System.out.println("===============================");

        o1 = null;
        System.gc();
        try {
			Thread.sleep(500);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}  // 保证一定被GC

        System.out.println(o1);
        System.out.println(weakReference.get());
        System.out.println(referenceQueue.poll());   // GC前会将引用放入队列
	}
	
	/**
	 * 虚引用：任何时候都有可能被回收
	 */
	public static void testPhantomReference() {
		 Object o1 = new Object();
        ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
        // 虚引用只有一个构造方法，必出传入引用队列
        PhantomReference<Object> phantomReference = new PhantomReference<>(o1, referenceQueue);

        System.out.println(o1);
        System.out.println(phantomReference.get());
        System.out.println("引用队列：" + referenceQueue.poll());

        // 使 o1 = null;可以 被垃圾回收
        o1 = null;
        System.out.println("==========gc后=============");
        System.gc();
        System.out.println(o1);
        System.out.println(phantomReference.get());
        System.out.println("引用队列：" + referenceQueue.poll());
	}
}
